package com.sa.gateway.config;

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.loadbalancer.*;
import org.springframework.cloud.loadbalancer.core.NoopServiceInstanceListSupplier;
import org.springframework.cloud.loadbalancer.core.ReactorServiceInstanceLoadBalancer;
import org.springframework.cloud.loadbalancer.core.SelectedInstanceCallback;
import org.springframework.cloud.loadbalancer.core.ServiceInstanceListSupplier;
import org.springframework.http.HttpHeaders;
import reactor.core.publisher.Mono;

import java.util.List;
import java.util.Random;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * 自定义负载均衡器，用于实现灰度发布策略。
 * 仅对特定路径进行灰度发布，其他路径采用默认负载均衡策略。
 *
 * @version 1.0
 * @website 
 */
@Slf4j
public class GrayLoadBalancer implements ReactorServiceInstanceLoadBalancer {
    private final String serviceId;  // 服务ID
    private AtomicInteger position;  // 轮询索引
    private ObjectProvider<ServiceInstanceListSupplier> serviceInstanceListSupplierProvider;  // 服务实例列表供应者

    public GrayLoadBalancer(ObjectProvider<ServiceInstanceListSupplier> serviceInstanceListSupplierProvider, String serviceId) {
        this.serviceId = serviceId;
        this.serviceInstanceListSupplierProvider = serviceInstanceListSupplierProvider;
        // 随机初始化轮询位置，避免每次服务启动时总是从相同的实例开始
        this.position = new AtomicInteger(new Random().nextInt(1000));
    }

    @Override
    public Mono<Response<ServiceInstance>> choose(Request request) {
        // 提供可用的服务实例列表
        ServiceInstanceListSupplier supplier = serviceInstanceListSupplierProvider
                .getIfAvailable(NoopServiceInstanceListSupplier::new);
        // 从实例列表中选择一个服务实例
        return supplier.get(request).next().map(serviceInstances ->
                this.processInstanceResponse(supplier, serviceInstances, request)
        );
    }

    /**
     * 处理选择服务实例的响应
     * @param supplier 服务实例供应者
     * @param serviceInstances 可用服务实例列表
     * @param request 当前请求
     * @return 选定的服务实例
     */
    private Response<ServiceInstance> processInstanceResponse(ServiceInstanceListSupplier supplier,
                                                              List<ServiceInstance> serviceInstances,
                                                              Request request) {
        // 根据请求选择具体的服务实例
        Response<ServiceInstance> serviceInstanceResponse = this.getInstanceResponse(serviceInstances, request);
        // 如果存在回调函数且选定了服务实例，执行回调
        if (supplier instanceof SelectedInstanceCallback && serviceInstanceResponse.hasServer()) {
            ((SelectedInstanceCallback) supplier).selectedServiceInstance(serviceInstanceResponse.getServer());
        }
        return serviceInstanceResponse;
    }

    /**
     * 根据请求和服务实例列表选择具体的服务实例
     * @param instances 服务实例列表
     * @param request 当前请求
     * @return 选定的服务实例响应
     */
    private Response<ServiceInstance> getInstanceResponse(List<ServiceInstance> instances, Request request) {
        // 如果服务实例列表为空，返回空响应
        if (instances.isEmpty()) {
            log.warn("服务不可用: " + this.serviceId);
            return new EmptyResponse();
        } else {
            // 从请求头中获取灰度标签
            RequestDataContext requestContext = (RequestDataContext) request.getContext();
            HttpHeaders headers = requestContext.getClientRequest().getHeaders();
            List<String> grayTagHeader = headers.get("Gray-Tag");

            if (grayTagHeader != null && !grayTagHeader.isEmpty() && "true".equals(grayTagHeader.get(0))) {
                // 如果请求头包含灰度标签，筛选灰度实例
                instances = filterGrayInstances(instances);
            } else {
                // 否则筛选正式节点
                instances = filterRegularInstances(instances);
            }

            // 使用轮询算法选择服务实例
            int pos = this.position.incrementAndGet() & Integer.MAX_VALUE;
            ServiceInstance instance = instances.get(pos % instances.size());
            return new DefaultResponse(instance);
        }
    }

    /**
     * 筛选灰度实例
     * @param instances 服务实例列表
     * @return 灰度服务实例列表
     */
    private List<ServiceInstance> filterGrayInstances(List<ServiceInstance> instances) {
        return instances.stream()
                .filter(instance -> "true".equals(instance.getMetadata().get("Gray-Tag")))
                .toList();
    }

    /**
     * 筛选正式实例
     * @param instances 服务实例列表
     * @return 正式服务实例列表
     */
    private List<ServiceInstance> filterRegularInstances(List<ServiceInstance> instances) {
        return instances.stream()
                .filter(instance -> instance.getMetadata().get("Gray-Tag") == null ||
                        !"true".equals(instance.getMetadata().get("Gray-Tag")))
                .toList();
    }
}
